use std::{fs, io::Read, path::Path};

use anyhow::Ok;
use base64::prelude::*;
use rand::rngs::OsRng;

use crate::{process_genpass, utils::get_reader, TextSignFormat};

use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};

pub fn process_text_sign(input: &str, key: &str, format: TextSignFormat) -> anyhow::Result<String> {
    let mut read = get_reader(input)?;
    let sign = match format {
        TextSignFormat::Blake3 => {
            let blake3 = Blake3::load(key)?;
            blake3.sign(&mut read)?
        }
        TextSignFormat::Ed25519 => {
            let ed25519 = Ed25519Signer::load(key)?;
            ed25519.sign(&mut read)?
        }
    };
    Ok(BASE64_STANDARD_NO_PAD.encode(sign))
}

pub fn process_text_verify(
    input: &str,
    key: &str,
    sign: &str,
    format: TextSignFormat,
) -> anyhow::Result<bool> {
    let mut read = get_reader(input)?;
    let sign = BASE64_STANDARD_NO_PAD.decode(sign)?;
    match format {
        TextSignFormat::Blake3 => {
            let blake3 = Blake3::load(key)?;
            blake3.verify(&mut read, sign.as_slice())
        }
        TextSignFormat::Ed25519 => {
            let ed25519_dalek = Ed25519Verifier::load(key)?;
            ed25519_dalek.verify(&mut read, &sign)
        }
    }
}

pub fn process_text_generate(format: TextSignFormat) -> anyhow::Result<Vec<Vec<u8>>> {
    match format {
        TextSignFormat::Blake3 => Blake3::generate(),
        TextSignFormat::Ed25519 => Ed25519Signer::generate(),
    }
}
struct Blake3 {
    key: [u8; 32],
}
impl Blake3 {
    fn new(key: [u8; 32]) -> Self {
        Self { key }
    }
    fn try_new(key: Vec<u8>) -> anyhow::Result<Self> {
        let key = &key[..32];
        let key = key.try_into()?;
        Ok(Self::new(key))
    }
}
struct Ed25519Signer {
    key: SigningKey,
}

trait KeyLoader {
    fn load(path: impl AsRef<Path>) -> anyhow::Result<Self>
    where
        Self: Sized;
}

impl KeyLoader for Blake3 {
    fn load(path: impl AsRef<Path>) -> anyhow::Result<Self> {
        let key = fs::read(path)?;
        Self::try_new(key)
    }
}
impl KeyLoader for Ed25519Signer {
    fn load(path: impl AsRef<Path>) -> anyhow::Result<Self> {
        let key = fs::read(path)?;
        Self::try_new(&key)
    }
}

impl Ed25519Signer {
    fn new(key: &[u8; 32]) -> Self {
        let key = SigningKey::from_bytes(key);
        Self { key }
    }
    fn try_new(key: &[u8]) -> anyhow::Result<Self> {
        let key = &key[..32];
        let key = key.try_into()?;
        Ok(Self::new(key))
    }
}
struct Ed25519Verifier {
    key: VerifyingKey,
}

impl KeyLoader for Ed25519Verifier {
    fn load(path: impl AsRef<Path>) -> anyhow::Result<Self> {
        let key = fs::read(path)?;
        Self::try_new(key)
    }
}

impl Ed25519Verifier {
    fn new(key: VerifyingKey) -> Self {
        Self { key }
    }
    fn try_new(key: impl AsRef<[u8]>) -> anyhow::Result<Self> {
        let key = key.as_ref();
        let key = &key[..32];
        let key = key.try_into()?;
        let key = VerifyingKey::from_bytes(key)?;
        Ok(Self::new(key))
    }
}
trait TextSign {
    fn sign(&self, read: &mut dyn Read) -> anyhow::Result<Vec<u8>>;
}

trait TextVerify {
    fn verify<R: Read>(&self, read: &mut R, sign: &[u8]) -> anyhow::Result<bool>;
}

///  generate a signature using the given key and input data by blake3 algorithm
///  returns the signature as a byte array
impl TextSign for Blake3 {
    fn sign(&self, read: &mut dyn Read) -> anyhow::Result<Vec<u8>> {
        let mut buf = Vec::new();
        read.read_to_end(&mut buf)?;
        let val = blake3::keyed_hash(&self.key, &buf).as_bytes().to_vec();
        Ok(val)
    }
}
///  verify a signature using the given key and input data by blake3 algorithm
///  returns true if the signature is valid, false otherwise d
impl TextVerify for Blake3 {
    fn verify<R: Read>(&self, read: &mut R, sign: &[u8]) -> anyhow::Result<bool> {
        let mut buf = Vec::new();
        read.read_to_end(&mut buf)?;
        let hash = blake3::keyed_hash(&self.key, &buf);
        Ok(hash.as_bytes() == sign)
    }
}

impl TextSign for Ed25519Signer {
    fn sign(&self, read: &mut dyn Read) -> anyhow::Result<Vec<u8>> {
        let mut buf = Vec::new();
        read.read_to_end(&mut buf)?;
        let sign = self.key.sign(&buf);
        Ok(sign.to_bytes().to_vec())
    }
}

impl TextVerify for Ed25519Verifier {
    fn verify<R: Read>(&self, read: &mut R, sign: &[u8]) -> anyhow::Result<bool> {
        let mut buf = Vec::new();
        read.read_to_end(&mut buf)?;
        let sign: &[u8; 64] = (&sign[..64]).try_into()?;
        let sign = Signature::from_bytes(sign);
        Ok(self.key.verify(&buf, &sign).is_ok())
    }
}

pub trait KeyGenerator {
    fn generate() -> anyhow::Result<Vec<Vec<u8>>>;
}

impl KeyGenerator for Blake3 {
    fn generate() -> anyhow::Result<Vec<Vec<u8>>> {
        // generate a new 32 length key
        let password = process_genpass(32, true, true, true, true)?;
        // transform the password  into vec[u8]
        let key = password.as_bytes().to_vec();
        Ok(vec![key])
    }
}

impl KeyGenerator for Ed25519Signer {
    fn generate() -> anyhow::Result<Vec<Vec<u8>>> {
        let mut csprng = OsRng;
        // generate a new 32 length key
        let sk: SigningKey = SigningKey::generate(&mut csprng);
        // first transform public key
        let pk = sk.verifying_key().to_bytes().to_vec();
        // second transform secret key
        let sk = sk.to_bytes().to_vec();
        Ok(vec![sk, pk])
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_blake3_sign_verify() -> anyhow::Result<()> {
        let data = b"hello world";
        let path = "fixtures/blake3.txt";
        let blake3 = Blake3::load(path)?;
        let sign = blake3.sign(&mut &data[..])?;
        assert!(blake3.verify(&mut &data[..], &sign)?);
        Ok(())
    }
    #[test]
    fn test_ed25519_sign_verify() -> anyhow::Result<()> {
        let path_pk = "fixtures/ed25519.pk";
        let path_sk = "fixtures/ed25519.sk";
        let pk = Ed25519Verifier::load(path_pk)?;
        let sk = Ed25519Signer::load(path_sk)?;
        let data = b"hello world";
        let sign = sk.sign(&mut &data[..])?;
        assert!(pk.verify(&mut &data[..], &sign).is_ok());
        Ok(())
    }
}
